package org.mmtk.plan.scale.colors;

import org.mmtk.plan.Phase;
import org.mmtk.plan.TransitiveClosure;
import org.mmtk.plan.scale.Scale;
import org.mmtk.plan.scale.utils.ScaleLogger;
import org.mmtk.utility.Log;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.Lock;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.Uninterruptible;

@Uninterruptible
public class SColors extends Scale {
	public static final int SCAN_COLOR = 2;
	public static final int PRIVATE_SCAN_COLOR = 3;
	public static final int SCAN_HELP = 4;
	/****************************************************************************
	 * Class variables
	 */
	private static int helperThreads;
	static int getHelperThreads() {
		return helperThreads;
	}
	private static int helperColors;
	static int getHelperColors() {
		return helperColors;
	}
	
	private static Lock helpersLock = VM.newLock("helperLock");
	private static void acquireHelperLock() {
		if(Options.scaleDebug.getValue() > 4) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.writeln("Trying to acquire helpersLock");
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
		helpersLock.acquire();
		if(Options.scaleDebug.getValue() > 4) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.writeln("Acquired helpersLock");
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
	}
	private static void releaseHelperLock() {
		helpersLock.release();
		if(Options.scaleDebug.getValue() > 4) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.writeln("Released helpersLock");
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
	}
	private static int tracersCompleteDone;
	private static long totalTracedDeadObjects;
	private static int tracersDone;
	private static int helpersDone;
	private static boolean madeColorsLive;
	private static final short MetrixColor_ClosurePhase = Phase.createComplex(
			"MetrixColor_ClosurePhase", 
			null, 
			Phase.scheduleMutator(PREPARE), 
			Phase.scheduleGlobal(PREPARE), 
			Phase.scheduleCollector(PREPARE),
			Phase.scheduleComplex(prepareStacks), 
			Phase.scheduleCollector(STACK_ROOTS), 
			Phase.scheduleCollector(ROOTS), 
			Phase.scheduleGlobal(ROOTS),
			Phase.scheduleGlobal(prepareClosure),
			Phase.scheduleCollector(prepareClosure),
			Phase.scheduleGlobal(CLOSURE), 
			Phase.scheduleCollector(depthClosure),
			Phase.scheduleGlobal(prepareClosure),
			Phase.scheduleCollector(prepareClosure),
			Phase.scheduleGlobal(CLOSURE), 
			Phase.scheduleCollector(fixClosure),
			Phase.scheduleGlobal(prepareClosure),
			Phase.scheduleCollector(prepareClosure),
			Phase.scheduleGlobal(CLOSURE),
			Phase.scheduleCollector(depthCheckClosure));
	
	protected static final int METRIX_COLOR_FLAVOR = 31;
	
	public SColors() {
		super();
	    
		Options.scHelpers = new SCHelpersOption();
		Options.scColors = new SCColorsOption();
		Options.scMaxColor = new SCMaxColorOption();
	}
	
	/*****************************************************************************
	 * Collection
	 */

	/**
	 * Perform a (global) collection phase.
	 * 
	 * @param phaseId
	 *            Collection phase to execute.
	 */
	@Inline
	@Override
	public void collectionPhase(short phaseId) {
		if (Options.scaleDebug.getValue() > 7) {
			Log.write("SColors \t collectionPhase entered with phaseId=");Log.write(phaseId);
			Log.write(", phaseName=");Log.writeln(Phase.getName(phaseId));
			Log.flush();
		}
		
		if (phaseId == PREPARE) {
			super.collectionPhase(phaseId);
			tracersDone = 0;
			helpersDone = 0;
			tracersCompleteDone = 0;
			totalTracedDeadObjects = 0;
			madeColorsLive = true;
			Helper.prepareHelpersTable();
			Color.prepareColorsTable();	
			return;
		}

		/*
		if (phaseId == prepareClosure) {
			if(isLastClosure()) {
				if(Options.scaleDebug.getValue() > 3) {
					Log.writeln("SColors \t collectionPhase - prepareClosure - before traceLiveColors");
					Color.dumpColorsTable();
				}		
				Color.traceLiveColors();
				if(Options.scaleDebug.getValue() > 3) {
					Log.writeln("SColors \t collectionPhase - prepareClosure - after traceLiveColors");
					Color.dumpColorsTable();
				}
			}
			super.collectionPhase(phaseId);					
			return;
		}
		
		if (phaseId == CLOSURE) {
			super.collectionPhase(phaseId);
			return;
		}*/
		
		/*
		if (phaseId == RELEASE) {
			super.collectionPhase(phaseId);
			Log.write("SColors release: totalTracedDeadObjects=");
			Log.writeln(totalTracedDeadObjects);
			return;
		}*/

		super.collectionPhase(phaseId);
	}
	
	@Override
	protected void doSetFirstClosureType() {
		if(traceFlavor == METRIX_COLOR_FLAVOR) {
			closure = CLOSURE_FIRST_SINGLE;
		}
	}
	
	@Override
	protected void doSetNextClosureType() {
		if (traceFlavor == METRIX_COLOR_FLAVOR) {
			if (closure == CLOSURE_FIRST_SINGLE) {
				closure = CLOSURE_CONCURRENT;
			} else if (closure == CLOSURE_CONCURRENT) {
				closure = CLOSURE_LAST_SINGLE;
			}
		}
	}
	
	@Override
	protected short getReplacementPhaseID() {
		if (traceFlavor == METRIX_COLOR_FLAVOR) {
			return MetrixColor_ClosurePhase;
		}
		return -1;
	}

	/*****************************************************************************
	 * Accounting
	 */

	/**
	 * Register specialized methods.
	 */
	@Interruptible
	@Override
	protected void registerSpecializedMethods() {
		TransitiveClosure.registerSpecializedScan(SCAN_COLOR, SColorsTraceLocal.class);
		TransitiveClosure.registerSpecializedScan(PRIVATE_SCAN_COLOR, SColorsTraceLocal.class);
		TransitiveClosure.registerSpecializedScan(SCAN_HELP, CMHelperTraceLocal.class);
		super.registerSpecializedMethods();
	}
	
	@Interruptible
	@Override
	protected void initializeRun() {
		
		traceFlavor = METRIX_COLOR_FLAVOR;
		
		super.initializeRun();
		
		//get user set number of helper threads, ensure it is less than number of collector threads
		helperThreads = Options.scHelpers.getValue();
		if(collectorThreads - helperThreads < 1) {
			helperThreads = collectorThreads - 1;
		}
		Helper.init();
		Log.write("helperThreads: ");Log.writeln(helperThreads);
			
		if(helperThreads == 0) {
			helperColors = 0;
		} else {
			//get user set number of helper colors, ensure it is no less than number of helper threads
			helperColors = Options.scColors.getValue();
			if(helperColors < helperThreads) {
				helperColors= helperThreads;
			}			
		}
		Log.write("helperColors: ");Log.writeln(helperColors);
		Color.init();
			
		scanner = new SColorsScanner();
		//scanner = null;
		//sweeper = new CMSweeper();
		sweeper = null;
		Log.flush();
	}
	
	public static void reportTraceDone(boolean helper) {
		acquireHelperLock();
		
		if(helper) {
			helpersDone++;
		} else {
			tracersDone++;
		}
		if(tracersDone+helpersDone == collectorThreads) {
			if(Options.scaleDebug.getValue() > 3) {
				Log.prependThreadId();
				Log.writeln("SColors.reportTraceDone - done");
			}		
			boolean madeLive = Color.traceLiveColors(false);
			if(Options.scaleDebug.getValue() > 3) {
				Log.write("SColors.reportTraceDone - after traceLiveColors, madeLive=");
				Log.writeln(madeLive);
				//Color.dumpColorsTable(true);
			}
		}
		
		releaseHelperLock();
	}
	
	public static void reportCompleteTraceDone(long tracedDeadObjects) {
		acquireHelperLock();
		
		tracersCompleteDone++;
		totalTracedDeadObjects += tracedDeadObjects;
		if(tracersCompleteDone%collectorThreads == 0) {
			if(Options.scaleDebug.getValue() > 3) {
				Log.write("SColors.reportCompleteTraceDone - checkpoint entered, tracersCompleteDone=");
				Log.writeln(tracersCompleteDone);
				//Color.dumpColorsTable(false);
			}
			madeColorsLive = Color.traceLiveColors(true);
			if(!madeColorsLive){
				if(Options.scaleDebug.getValue() > 3) {
					Log.writeln("SColors.reportCompleteTraceDone - after the last Color.traceLiveColors");
					Color.dumpColorsTable(true);
				}
				Log.write("totalTracedDeadObjects: ");
				Log.writeln(totalTracedDeadObjects);
				Log.flush();
			} else {
				tracedDeadObjects = 0;
				if(Options.scaleDebug.getValue() > 3) {
					Log.writeln("SColors.reportCompleteTraceDone - colors made live, will proceed");
					//Color.dumpColorsTable(false);
				}
			}
		}
		
		releaseHelperLock();
	}
	
	public static boolean tracersDone() {
		boolean result = false;
		
		acquireHelperLock();
		
		if(tracersDone >= collectorThreads - helperThreads) {
			result = true;
		}
		
		releaseHelperLock();
		
		return result;
	}
	
	public static boolean isTraceDone() {
		boolean result = false;
		
		acquireHelperLock();
		
		if(tracersDone >= collectorThreads - helperThreads && 
				helpersDone >= helperThreads) {
			result = true;
		}
		if (Options.scaleDebug.getValue() > 7) {
			Log.prependThreadId();
			Log.write("isTraceDone - collectorThreads=");Log.write(collectorThreads);
			Log.write(", helperThreads=");Log.write(helperThreads);
			Log.write(", tracersDone=");Log.write(tracersDone);
			Log.write(", result=");Log.writeln(result);
		}
		
		releaseHelperLock();
		
		return result;
	}
	
	public boolean isCompleteTraceDone() {
		boolean result = false;
		
		acquireHelperLock();
		
		if(tracersCompleteDone%collectorThreads == 0) {
			result = true;
		}
		
		releaseHelperLock();
		
		return result;
	}

	public static boolean isMadeColorsLive() {
		return madeColorsLive;
	}
}
